package procsim;

import java.util.*;

public class BigMSignal implements Sig, Updateable {
    private int numOfBits;
    private Signal[] bits;
    private String name;
    private ArrayList<Updateable> elements;
    private LinkedHashMap<Sig, Integer> insigs, outsigs;
    private void update(boolean apply){
        if(insigs!=null){
            Iterator iter = insigs.keySet().iterator();
            while(iter.hasNext()){
                Sig sig = (Sig)iter.next();
                if(sig instanceof Signal){
                    Signal ssig = (Signal)sig;
                    int index = insigs.get(ssig);
                    bits[index].set(sig.get(), false);
                } else {
                    BigMSignal msig = (BigMSignal)sig;
                    Signal[] sigar = msig.getBits();
                    int sindex = insigs.get(msig);
                    for(int i=0; i<sigar.length; i++)
                        bits[sindex+i].set(sigar[i], false);
                }
            }
        }
        if(outsigs!=null){
            Iterator iter = outsigs.keySet().iterator();
            while(iter.hasNext()){
                Sig sig = (Sig)iter.next();
                if(sig instanceof Signal){
                    Signal ssig = (Signal)sig;
                    ssig.set(bits[outsigs.get(ssig)]);
                } else {
                    BigMSignal msig = (BigMSignal)sig;
                    int sindex = outsigs.get(msig);
                    for(int i=0; i<msig.getNumOfBits(); i++)
                        msig.set(i, bits[sindex+i].get());
                }
            }
        }
        if(apply && elements!=null) applySignalToElements();
    }
    public BigMSignal(int numOfBits){
        this.numOfBits=numOfBits;
        bits = new Signal[numOfBits];
        for(int i=0; i<numOfBits; i++)
            bits[i] = new Signal(0);
    }
    public BigMSignal(int numOfBits, String nam){
        this(numOfBits);
        name=nam;
    }
    public BigMSignal(int numOfBits, int vrednost){
        this.numOfBits=numOfBits;
        bits = Util.intArray2SignalArray(Util.int2BinaryArray(vrednost, numOfBits));
    }
    public BigMSignal(int numOfBits, int vrednost, String nam){
        this(numOfBits, vrednost);
        name=nam;
    }
    /*
     * Ne vraca pravu vrednost, vec samo po modulu 32; ne koristiti!
     */
    @Override
    public int get(){
        return Util.binaryArray2Int(Util.signalArray2IntArray(bits));
    }
    public Signal[] getBits(){
        return bits;
    }
    public Signal getBit(int index){
        if(index>=0 && index<numOfBits)
            return bits[index];
        else {
            System.err.println("Pristup nepostojecem bitu neuspesan!");
            return null;
        }
    }
    @Override
    public BigMSignal not(){
        for(int i=0; i<numOfBits; i++)
            bits[i].set(Math.abs(bits[i].get()-1), false);
        update(true);
        return this;
    }
    @Override
    public String toString(){
        String ret = name+":";
        int numOfSteps=numOfBits/4;
        int rest=numOfBits%4;
        int[] arr = new int[numOfSteps+(rest>0 ? 1 : 0)];
        for(int i=0; i<numOfSteps; i++){
            int tmp=0;
            for(int j=0; j<4; j++)
                tmp+=bits[4*i+j].get()<<j;
            arr[i]=tmp;
        }
        if(rest>0){
            int tmp=0;
            for(int i=0; i<rest; i++)
                tmp+=bits[numOfSteps].get()<<i;
            arr[arr.length-1]=tmp;
        }
        for(int i=0; i<arr.length; i++)
            ret+=Util.int2Hex(arr[arr.length-1-i], 1);
        return ret;
    }
    @Override
    public String getName(){
        return name;
    }
    @Override
    public BigMSignal setName(String str){
        name = str;
        return this;
    }
    @Override
    public boolean isNull(){
        for(int i=0; i<numOfBits; i++)
            if(!bits[i].isNull()) return false;
        return true;
    }
    public int getNumOfBits(){
        return numOfBits;
    }
    public BigMSignal set(Signal[] sig){
        return set(sig, true);
    }
    public BigMSignal set(BigMSignal sig){
        return set(sig, true);
    }
    public BigMSignal set(int index, int sig){
        return set(index, sig, true);
    }
    public BigMSignal set(BigMSignal sig, boolean apply){
        return set(sig.getBits(), true);
    }
    public BigMSignal set(Signal[] sig, boolean apply){
        numOfBits=sig.length;
        for(int i=0; i<numOfBits; i++)
            bits[i].set(sig[i], false);
        update(apply);
        return this;
    }
    public BigMSignal set(int index, int sig, boolean apply){
        if(bits[index].get()==sig) return this;
        bits[index].set(sig);
        update(apply);
        return this;
    }
    public BigMSignal bindOut(int index, Signal sig){
        if(index>=0 && index<numOfBits){
            if(outsigs==null) outsigs = new LinkedHashMap<Sig, Integer>();
            outsigs.put(sig, index);
        } else System.err.println("Nemoguce uraditi bindOut za Signal "+sig+"!");
        return this;
    }
    public BigMSignal bindOut(int index, BigMSignal sig){
        if(index>=0 && index+sig.getNumOfBits()-1<numOfBits){
            if(outsigs==null) outsigs = new LinkedHashMap<Sig, Integer>();
            outsigs.put(sig, index);
        } else System.err.println("Nemoguce uraditi bindOut za MSignal "+sig+"!");
        return this;
    }
    public BigMSignal bindOut(int startindex, int endindex, Signal[] sig){
        if(startindex>=0 && startindex<numOfBits && endindex>=0 && endindex<numOfBits && startindex<endindex && endindex-startindex+1==sig.length){
            if(outsigs==null) outsigs = new LinkedHashMap<Sig, Integer>();
            for(int i=startindex; i<=endindex; i++)
                outsigs.put(sig[i-startindex], i);
        } else System.err.println("Nemoguce uraditi bindOut za Signal "+sig+"!");
        return this;
    }
    public BigMSignal bindIn(int index, Signal sig){
        if(index>=0 && index<numOfBits){
            sig.addElement(this);
            if(insigs==null) insigs = new LinkedHashMap<Sig, Integer>();
            insigs.put(sig, index);
        } else System.err.println("Nemoguce uraditi bindIn za Signal "+sig+"!");
        return this;
    }
    public BigMSignal bindIn(int index, BigMSignal sig){
        if(index>=0 && index<numOfBits){
            sig.addElement(this);
            if(insigs==null) insigs = new LinkedHashMap<Sig, Integer>();
            insigs.put(sig, index);
        } else System.err.println("Nemoguce uraditi bindIn za MSignal "+sig+"!");
        return this;
    }
    public BigMSignal bindIn(int startindex, int endindex, Signal[] sig){
        if(startindex>=0 && startindex<numOfBits && endindex>=0 && endindex<numOfBits && startindex<endindex && endindex-startindex+1==sig.length){
            if(insigs==null) insigs = new LinkedHashMap<Sig, Integer>();
            for(int i=startindex; i<=endindex; i++){
                sig[i].addElement(this);
                insigs.put(sig[i], i);
            }
        } else System.err.println("Nemoguce uraditi bindIn za Signal "+sig+"!");
        return this;
    }
    @Override
    public void updateSignal(Sig sig) {
        if(insigs==null) return;
        boolean changed = false;
        Iterator iter = insigs.keySet().iterator();
        while(iter.hasNext()){
            Sig tmp = (Sig)iter.next();
            if(sig==tmp) changed=true;
        }
        if(changed) update(true);
    }
    @Override
    public BigMSignal addElement(Updateable elem) {
        if(elements==null)
            elements = new ArrayList<Updateable>();
        if(!elements.contains(elem))
            elements.add(elem);
        return this;
    }
    private void applySignalToElements(){
        if(elements==null) return;
        Iterator iter = elements.iterator();
        while(iter.hasNext())
            ((Updateable)iter.next()).updateSignal(this);
    }
}
